#include "output.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>

static FILE* asmfile = NULL;
static FILE* binfile = NULL;
static int cursor = 0;

char dummy[3] = {};

// commenter les sorties non voulues
//#define OUT_STDOUT
#define OUT_ASM // pas encore désactivables
#define OUT_BMM // 

#ifdef OUT_STDOUT
  void stdout_print(const char* f, ...) {
    va_list o;
	 va_start(o, f);
	 vprintf(f, o);
	 va_end(o);
  }
#else
  void stdout_print(const char* f, ...) {}
#endif


/*** Output ***/

int output_open(const char* name)
{
	char asmname[128];
	char binname[128];
	strncpy(asmname, name, 128);
	strncpy(binname, name, 128);
	strncat(asmname, ".asm", 128);
	strncat(binname, ".bmm", 128);
	
	asmfile = fopen(asmname, "w+b");
	if (!asmfile) {
		perror("Can't open output file :");
		return -1;
	}	
	binfile = fopen(binname, "w+b");
	if (!binfile) {
		fclose(asmfile);
		perror("Can't open output file :");
		return -1;
	}
	
	return 0;
}

void output_close()
{
	fclose(asmfile);
	fclose(binfile);
}


void out(asm_Operation op, ...)
{
	va_list operands;
	int i;
   char binop[4];
	
	fprintf(asmfile, asm_blank);
	fseek(asmfile, -ASM_LINE_LEN, SEEK_CUR);
	
	stdout_print("%d: %s", cursor, asm_ops[op].name);
	fprintf(asmfile, asm_ops[op].name);
	binop[0] = op;
	
	va_start(operands, op);
	for (i=0; i < asm_ops[op].nbOperand; i++) 
	{
		Addr v = va_arg(operands, Addr);
      char ca = (v.space == LOCAL) ? 's' : 'g';
		stdout_print(" %c%d", ca, v.addr);
		fprintf(asmfile, " %c%d", ca, v.addr);
		binop[i+1] = v.addr;
      if (v.space == LOCAL)
         binop[0] |= 1 << (7-i);
	}
	va_end(operands);
	
	stdout_print("\n");
	fwrite(binop, 4, 1, binfile);
	
	cursor++;
	fseek(asmfile, ASM_LINE_LEN * cursor, SEEK_SET);
}

void out_at(int index, asm_Operation op, ...)
{
	va_list operands;
	int i;
   char binop[4];
	
	assert(index < cursor); // ensure space is left blank and formatted
	stdout_print("^^^ filling at %d: %s", index, asm_ops[op].name);
	
	fseek(asmfile, ASM_LINE_LEN * index, SEEK_SET);
	fseek(binfile, 4 * index, SEEK_SET);
	fprintf(asmfile, asm_ops[op].name);
   binop[0] = op;
	
	va_start(operands, op);
	for (i=0; i < asm_ops[op].nbOperand; i++) 
	{
		Addr v = va_arg(operands, Addr);
      char ca = (v.space == LOCAL) ? 's' : 'g';
		stdout_print(" %c%d", ca, v.addr);
		fprintf(asmfile, " %c%d", ca, v.addr);
		binop[i+1] = v.addr;
      if (v.space == LOCAL)
         binop[0] |= 1 << (7-i);
	}
	va_end(operands);
   	
   stdout_print("\n");
	fwrite(binop, 4, 1, binfile);
	
	fseek(binfile, 4*cursor, SEEK_SET);
	fseek(asmfile, ASM_LINE_LEN*cursor, SEEK_SET);
}


static const char* fmts[] = {"", "%s %c%d", "%s %c%d %c%d", "%s %c%d %c%d %c%d"};
static int lastChDest = -1;

void output_changeDest(Addr addr)
{
   
   char instr[4];
   int i;
   const asm_Op* op; 
   
   // relit le fichier binaire (plus facile) pour reconstruire l'instruction
   
   char ca = (addr.space == LOCAL) ? 's' : 'g';
   stdout_print("^ correct last destination to %c%d\n", ca, addr.addr);
   lastChDest = cursor;
   
   fseek(binfile, 4 * (cursor-1), SEEK_SET);
	fseek(asmfile, ASM_LINE_LEN * (cursor-1), SEEK_SET);
   
   fread(instr, 4, 1, binfile);
   fprintf(asmfile, asm_blank);
   fseek(binfile, 4 * (cursor-1), SEEK_SET);
   fseek(asmfile, ASM_LINE_LEN * (cursor-1), SEEK_SET);
   instr[1] = addr.addr;
   if (addr.space == LOCAL) 
      instr[0] |= ADDR_A;
   else
      instr[0] &= ~ADDR_A;
   
   op = &(asm_ops[instr[0] & 0x0F]);
   
	fwrite(instr, 4, 1, binfile);
   char cb = (instr[0] & ADDR_B) ? 's' : 'g';
   char cc = (instr[0] & ADDR_C) ? 's' : 'g';
	fprintf(asmfile, fmts[op->nbOperand], 
                     op->name, ca, instr[1], cb, instr[2], cc, instr[3]);
   fseek(asmfile, ASM_LINE_LEN * (cursor), SEEK_SET);
}

int output_justUsedChangeDest()
{
   return lastChDest == cursor;
}


typedef char line[ASM_LINE_LEN];

void output_inverse(int debut, int milieu, int fin)
{
   int cnt = fin - debut;
   int c1 = (milieu - debut);
   int c2 = (fin - milieu);
   line* ta1 = (line*)malloc(ASM_LINE_LEN * cnt);
   line* ta2 = (line*)malloc(ASM_LINE_LEN * cnt);
   int* tb1 = (int*)malloc(4*cnt);
   int* tb2 = (int*)malloc(4*cnt);
   
   stdout_print("^ inverse:%d:%d:%d\n", debut, milieu, fin);
   
   fseek(asmfile, debut * ASM_LINE_LEN, SEEK_SET);
   fread(ta1, ASM_LINE_LEN, cnt, asmfile);
   memcpy(ta2 + c2, ta1, c1 * sizeof(line));
   memcpy(ta2, ta1 + c1, c2 * sizeof(line));
   fseek(asmfile, debut * ASM_LINE_LEN, SEEK_SET);
   fwrite(ta2, ASM_LINE_LEN, cnt, asmfile);
   fseek(asmfile, ASM_LINE_LEN * (cursor), SEEK_SET);
   
   fseek(binfile, 4 * debut, SEEK_SET);
   fread(tb1, 4, cnt, binfile);
   memcpy(tb2 + c2, tb1, c1 * 4);
   memcpy(tb2, tb1 + c1, c2 * 4);
   fseek(binfile, 4 * debut, SEEK_SET);
   fwrite(tb2, 4, cnt, binfile);
   fseek(binfile, 4 * (cursor), SEEK_SET);
   
   free(ta1);
   free(ta2);
   free(tb1);
   free(tb2);
}


int output_cur()
{
	return cursor;
}

int output_later()
{
	stdout_print("[ Hole at %d ]\n", cursor);
	fprintf(asmfile, asm_blank);
	
	cursor++;
	fseek(binfile, 4 * cursor, SEEK_SET);
	return cursor - 1;
}

void output_unput()
{
	stdout_print("unput\n");
	cursor--;
	fseek(binfile, 4*cursor, SEEK_SET);
	fseek(asmfile, ASM_LINE_LEN*cursor, SEEK_SET);
}
